using Cobra11Undercover.Material;
using Engine.Graphics;
using SharpDX;
using System;
using System.IO;

namespace Engine.FileFormats3D.C11Undercover.QADFile
{
    public struct C11Surface
    {
        #region Internal Fields

        internal short bumpTexture1;
        internal short bumpTexture2;
        internal short bumpTexture3;
        internal short data1;
        internal short mappingMode;
        internal short texture1ID;
        internal short texture2ID;
        internal short texture3ID;

        #endregion Internal Fields

        #region Private Fields

        private uint data5;
        private uint data6;
        private int hashcode;
        private float matrix1;
        private float matrix2;
        private float matrix3;
        private float matrix6;
        private float matrix7;
        private long positionInFile;
        private Vector4 texUVMod1;
        private Vector4 texUVMod2;

        #endregion Private Fields

        #region Public Constructors

        public C11Surface(C11Material material, MaterialManager materialManager)
        {
            this.texture1ID = (short)(material.TextureID1);
            this.texture2ID = (short)(material.TextureID2);
            this.texture3ID = (short)(material.TextureID3);
            this.data1 = 0xFF;
            this.bumpTexture1 = 0xFF;
            this.bumpTexture2 = 0xFF;
            this.bumpTexture3 = 0xFF;
            this.mappingMode = (short)material.MappingMode;
            this.matrix1 = 0.0f;
            this.matrix2 = 0.0f;
            this.matrix3 = 0.0f;
            this.texUVMod1 = new Vector4(0, 0, 1, 1);
            this.matrix6 = 0.0f;
            this.matrix7 = 0.0f;
            this.texUVMod2 = new Vector4(0, 0, 1, 1);
            this.data5 = 0;
            this.data6 = 0;
            this.positionInFile = 0;
            hashcode = 0;
            UpdateHashCode();
        }

        public C11Surface(short textureId1, short textureId2, short textureId3, short data1,
            short bumpTexture1, short bumpTexture2, short bumpTexture3, short mappingMode, float matrix1, float matrix2,
            float matrix3, float texUVMod1Z, float texUVMod1W, float matrix6, float matrix7, float texUVMod2Z,
            float texUVMod2W, uint data5, uint data6)
        {
            this.positionInFile = 0;

            this.texture1ID = textureId1;
            this.texture2ID = textureId2;
            this.texture3ID = textureId3;
            this.data1 = data1;
            this.bumpTexture1 = bumpTexture1;
            this.bumpTexture2 = bumpTexture2;
            this.bumpTexture3 = bumpTexture3;
            this.mappingMode = mappingMode;
            this.matrix1 = matrix1;
            this.matrix2 = matrix2;
            this.matrix3 = matrix3;
            this.texUVMod1 = new Vector4(0, 0, texUVMod1Z, texUVMod1W);
            this.matrix6 = matrix6;
            this.matrix7 = matrix7;
            this.texUVMod2 = new Vector4(0, 0, texUVMod2Z, texUVMod2W);
            this.data5 = data5;
            this.data6 = data6;
            this.hashcode = 0;
            UpdateHashCode();
        }

        #endregion Public Constructors

        #region Public Properties

        public Vector4 TexUVMod1
        {
            get { return texUVMod1; }
            set { texUVMod1 = value; }
        }

        public Vector4 TexUVMod2
        {
            get { return texUVMod2; }
            set { texUVMod2 = value; }
        }

        #endregion Public Properties

        #region Public Methods

        public static C11Surface FromStream(BinaryReader br)
        {
            long positionInFile = br.BaseStream.Position;

            C11Surface surface = new C11Surface(
                br.ReadInt16(), // tex1
                br.ReadInt16(), // tex2
                br.ReadInt16(), // tex3
                br.ReadInt16(), // data
                br.ReadInt16(), // bumpTexture1
                br.ReadInt16(), // bumpTexture2
                br.ReadInt16(), // bumpTexture3
                br.ReadInt16(), // mappingMode
                br.ReadSingle(),// matrix
                br.ReadSingle(),// matrix
                br.ReadSingle(),// matrix
                br.ReadSingle(),// matrix
                br.ReadSingle(),// matrix
                br.ReadSingle(),// matrix
                br.ReadSingle(),// matrix
                br.ReadSingle(),// matrix
                br.ReadSingle(),// matrix
                br.ReadUInt32(), // data
                br.ReadUInt32()); // data
            surface.positionInFile = positionInFile;

            if (surface.mappingMode == 32)
            {
            }

            return surface;
        }

        public override int GetHashCode()
        {
            return this.hashcode;
        }

        /// <summary>
        /// Converts the c11 surface to a Material which can used in rendering engine.
        /// </summary>
        /// <param name="materialManager"></param>
        /// <param name="offsetForBumbTextures">is the offset at which index the bump texturenames begin in materialManager.</param>
        /// <returns></returns>
        public C11Material ToMaterial(MaterialManager materialManager, QadFile qadFile)
        {
            if (this.texture1ID > materialManager.TextureNames.Count - 1)
                this.texture1ID = 0;
            if (this.texture2ID > materialManager.TextureNames.Count - 1)
                this.texture2ID = 0;
            if (this.texture3ID > materialManager.TextureNames.Count - 1)
                this.texture3ID = 0;

            string bumpTextureName1 = qadFile.GetBumpTextureName(this.bumpTexture1);// (this.bumpTexture1 < materialManager.TextureNames.Count - 1 && this.bumpTexture1 > -1) ? materialManager.TextureNames[(int)this.bumpTexture1 + offsetForBumbTextures] : string.Empty;
            string bumpTextureName2 = qadFile.GetBumpTextureName(this.bumpTexture2);//(this.bumpTexture2 < materialManager.TextureNames.Count - 1 && this.bumpTexture2 > -1) ? materialManager.TextureNames[(int)this.bumpTexture2 + offsetForBumbTextures] : string.Empty;
            string bumpTextureName3 = qadFile.GetBumpTextureName(this.bumpTexture3);//(this.bumpTexture3 < materialManager.TextureNames.Count - 1 && this.bumpTexture3 > -1) ? materialManager.TextureNames[(int)this.bumpTexture3 + offsetForBumbTextures] : string.Empty;

            C11Material material = new C11Material(materialManager.TextureNames[(int)this.texture1ID], materialManager.TextureNames[(int)this.texture2ID], materialManager.TextureNames[(int)this.texture3ID],
                bumpTextureName1,
                bumpTextureName2,
                bumpTextureName3,
                (EnumMappingMode)this.mappingMode, this.TexUVMod1, this.TexUVMod1, materialManager);

            return material;
            //return new Material(materialManager.TextureNames[(int)this.texture1ID], materialManager.TextureNames[(int)this.texture2ID], materialManager.TextureNames[(int)this.texture3ID], (EnumMappingMode)this.mappingMode, this.mappings1, this.mappings2, this.mappings3);
        }

        public override string ToString()
        {
            //return string.Format("T1: {0}, T2: {1}, T3: {2}, Mappint: {3}", texture1ID, texture2ID, texture3ID, mappingMode);

            //return string.Format("{0} \t {1} \t {2} \t {3} \t {4} \t {5} ", this.data1.ToString(), this.bumpTexture1.ToString(), this.bumpTexture2.ToString(), this.bumpTexture3.ToString(), this.data5.ToString(), this.data6.ToString());

            //return string.Format("{0} \t {1} \t {2} \t {3} \t {4} \t {5} \t {6} \t {7} \t {8}\tmm:{9}", this.mappingMatrix3x3.M11, this.mappingMatrix3x3.M12, this.mappingMatrix3x3.M13
            //    , this.mappingMatrix3x3.M21, this.mappingMatrix3x3.M22, this.mappingMatrix3x3.M23
            //    , this.mappingMatrix3x3.M31, this.mappingMatrix3x3.M32, this.mappingMatrix3x3.M33, this.mappingMode);

            return String.Format("{0:F2} \t {1:F2} \t {2:F2} \t {3:F2} \t {4:F2} \t {5:F2} \t {6:F2} \t {7:F2} \t {8:F2}\tmm: {9} \td1:{10} \td5:{11} \td6:{12}", this.matrix1, this.matrix2, this.matrix3
                   , this.texUVMod1.Z, this.texUVMod1.W, this.matrix6
                   , this.matrix7, this.texUVMod2.Z, this.texUVMod2.W, this.mappingMode, this.data1, this.data5, this.data6);
        }

        #endregion Public Methods

        #region Internal Methods

        internal void ToStream(BinaryWriter bw)
        {
            if (false)
            {
                bw.Write((short)this.texture1ID);  // br.ReadInt16(), // tex1 // 0
                bw.Write((short)1);    // br.ReadInt16(), // tex2 // 2
                bw.Write((short)2);  // br.ReadInt16(), // tex3 // 4
                bw.Write((short)this.data1);   // br.ReadInt16(), // data // 6
            }
            else
            {
                bw.Write((short)this.texture1ID);  // br.ReadInt16(), // tex1 // 0
                bw.Write((short)this.texture2ID);    // br.ReadInt16(), // tex2 // 2
                bw.Write((short)this.texture3ID);  // br.ReadInt16(), // tex3 // 4
                bw.Write((short)this.data1);   // br.ReadInt16(), // data // 6
            }
            if (false)
            {
                bw.Write((short)-1);
                bw.Write((short)-1);
                bw.Write((short)-1);
            }
            else
            {
                bw.Write((short)this.bumpTexture1);   // br.ReadInt16(), // bumpTexture1 // 8
                bw.Write((short)this.bumpTexture2);  // br.ReadInt16(), // bumpTexture2 // 10
                bw.Write((short)this.bumpTexture3);  // br.ReadInt16(), // bumpTexture3 // 12
            }

            if (false) // Debug mappingMode
                bw.Write((short)32);
            else
                bw.Write((short)this.mappingMode);  // br.ReadInt16(), // mappingMode // 14

            if (true)
            {
                bw.Write((float)0.0f);
                bw.Write((float)0.0f);
                bw.Write((float)0.0f);
            }
            else
            {
                bw.Write((float)this.matrix1);   // br.ReadSingle(),// matrix // 16
                bw.Write((float)this.matrix2);   // br.ReadSingle(),// matrix // 20
                bw.Write((float)this.matrix3);  // br.ReadSingle(),// matrix // 24
            }

            if (true) // second texture
            {
                bw.Write((float)0.1f);
                bw.Write((float)0.1f);
                bw.Write((float)0.01f);
                bw.Write((float)0.01f);
            }
            else
            {
                bw.Write(this.texUVMod1.Z);// br.ReadSingle(),// matrix // 28
                bw.Write(this.texUVMod1.W);// br.ReadSingle(),// matrix // 32
                bw.Write((float)this.matrix6); // br.ReadSingle(),// matrix // 36
                bw.Write((float)this.matrix7); // br.ReadSingle(),// matrix // 40
            }
            if (true)
            {
                bw.Write((float)0.01f);
                bw.Write((float)0.01f);
                bw.Write((int)204900);
                bw.Write((int)204900);
            }
            else
            {
                bw.Write(this.texUVMod2.Z); // br.ReadSingle(),// matrix // 44
                bw.Write(this.texUVMod2.W); // br.ReadSingle(),// matrix // 48
                bw.Write((int)this.data5);  // br.ReadUInt32(), // data // 52
                bw.Write((int)this.data6);  // br.ReadUInt32()); // data // 54
            }
        }

        #endregion Internal Methods

        #region Private Methods

        private void UpdateHashCode()
        {
            AdlerChecksum adler = new AdlerChecksum();
            byte[] byteArray = new byte[60];
            BitConverter.GetBytes(this.texture1ID).CopyTo(byteArray, 0);
            BitConverter.GetBytes(this.texture2ID).CopyTo(byteArray, 2);
            BitConverter.GetBytes(this.texture3ID).CopyTo(byteArray, 4);
            BitConverter.GetBytes(this.data1).CopyTo(byteArray, 6);
            BitConverter.GetBytes(this.bumpTexture1).CopyTo(byteArray, 8);
            BitConverter.GetBytes(this.bumpTexture2).CopyTo(byteArray, 10);
            BitConverter.GetBytes(this.bumpTexture3).CopyTo(byteArray, 12);
            BitConverter.GetBytes(this.mappingMode).CopyTo(byteArray, 14);
            BitConverter.GetBytes(this.matrix1).CopyTo(byteArray, 16);
            BitConverter.GetBytes(this.matrix2).CopyTo(byteArray, 20);
            BitConverter.GetBytes(this.matrix3).CopyTo(byteArray, 24);
            BitConverter.GetBytes(this.TexUVMod1.Z).CopyTo(byteArray, 28);
            BitConverter.GetBytes(this.TexUVMod1.W).CopyTo(byteArray, 32);
            BitConverter.GetBytes(this.matrix6).CopyTo(byteArray, 36);
            BitConverter.GetBytes(this.matrix7).CopyTo(byteArray, 40);
            BitConverter.GetBytes(this.TexUVMod2.Z).CopyTo(byteArray, 44);
            BitConverter.GetBytes(this.TexUVMod2.W).CopyTo(byteArray, 48);
            BitConverter.GetBytes(this.data5).CopyTo(byteArray, 52);
            BitConverter.GetBytes(this.data6).CopyTo(byteArray, 56);

            adler.MakeForBuff(byteArray);
            this.hashcode = (int)adler.ChecksumValue;
        }

        #endregion Private Methods
    }
}